package com.huitone.smspfm.socket.core;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.Date;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.bin.common.utils.StringUtils;
import com.huitone.smspfm.socket.model.AccountInfo;
import com.huitone.smspfm.socket.server.SocketHolderManager;
import com.huitone.smspfm.socket.utils.SocketUtils;

/**   
* @Title SocketThread.java 
* @Package com.huitone.smspfm.socket.server 
* @Description TODO(用一句话描述该文件做什么) 
* @author ybliang   
* @date 2017年3月25日 下午8:05:41 
* @version V1.0   
*/
public class SocketHolder implements Runnable {
	
	private static final Logger logger = LogManager.getLogger(SocketHolder.class);
	
	private AccountInfo accountInfo = new AccountInfo();

	private Socket accept = null;
	
	private AbstractMessageHandler messageHandler = null;
	
	private AbstractUserChecker userChecker = null;
	
	private MessageDispacher messageDispacher = new MessageDispacher();
	
	/**
	 * 上一次流读取时间
	 */
	private long lastReadTimeMillis = 0;
	
	private int soTimeOut = 30000;
	
	/**
	 * 是否用于服务端
	 */
	private boolean forServer = false;
	
	public SocketHolder(){}
	
	public SocketHolder(Socket accept, AbstractMessageHandler messageHandler) {
		super();
		this.accept = accept;
		this.messageHandler = messageHandler;
		if (this.accept != null) {
			accountInfo.setInetAddress(this.accept.getInetAddress());
		}
	}
	
	public SocketHolder(Socket accept, AbstractUserChecker userChecker, AbstractMessageHandler messageHandler) {
		super();
		this.accept = accept;
		this.messageHandler = messageHandler;
		this.userChecker = userChecker;
		if (this.accept != null) {
			accountInfo.setInetAddress(this.accept.getInetAddress());
		}
	}
	
	@Override
	public void run() {
		
		try {
			accept.setKeepAlive(true);
		} catch (SocketException e1) {
			logger.error("SocketHolder Socket setKeepAlive(true) 时发生错误导致异常关闭", e1);
			try {
				accept.close();
			} catch (IOException e) {
				logger.error("SocketHolder Socket 调用close()方法时发生异常", e);
			}
			return;
		}
		
		while(!Thread.currentThread().isInterrupted() && accept != null && !accept.isClosed()) {
			
			lastReadTimeMillis = System.currentTimeMillis();
			
			try {
				messageDispacher.readMessage(this);
			} catch (Exception e) {
				if (e instanceof SocketException && StringUtils.equals("Socket closed", e.getMessage())) {
					if (accountInfo.isLogin()) {
						logger.info(accountInfo.getSrvNode() + "连接关闭");
					} else {
						logger.debug("连接关闭" + (accountInfo.getInetAddress() != null ? (",远程IP:" + accountInfo.getInetAddress().getHostAddress()) : ""));
					}
				} else if (e instanceof SocketTimeoutException) {
					logger.info(accountInfo.getSrvNode() + "连接空闲时间超时关闭");
				} else if (e instanceof InterruptedException){
					logger.info(accountInfo.getSrvNode() + "连接中断关闭");
				} else {
					logger.warn(accountInfo.getSrvNode() + "连接异常关闭:" + e.getMessage());
				}
				break;
			}
		}
		
		close();
		
		if (forServer) {
			SocketHolderManager.getInstance().unbind(this);
		}
	}
	
	public synchronized void write(byte[] message) throws IOException {
		SocketUtils.write(message, getOutputStream());
		setLastReadTimeMillis(System.currentTimeMillis());
	}
	
	public OutputStream getOutputStream() throws IOException {
		return accept.getOutputStream();
	}
	
	public boolean isConnected() {
		return SocketUtils.isConnected(accept);
	}
	
	public void close() {
		if (accept != null) {
			try {
				accept.close();
				accept = null;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public Socket getAccept() {
		return accept;
	}

	public AbstractMessageHandler getMessageHandler() {
		return messageHandler;
	}

	public void setMessageHandler(AbstractMessageHandler messageHandler) {
		this.messageHandler = messageHandler;
	}
	
	public long getFreeTimeMillis() {
		
		long time = new Date().getTime();
		
		long freeTime = time - lastReadTimeMillis;
		
		return freeTime;
	}
	
	public long getLastReadTimeMillis() {
		return lastReadTimeMillis;
	}

	public void setLastReadTimeMillis(long lastReadTimeMillis) {
		this.lastReadTimeMillis = lastReadTimeMillis;
	}

	public int getSoTimeOut() {
		return soTimeOut;
	}

	public SocketHolder setSoTimeOut(int soTimeOut) {
		this.soTimeOut = soTimeOut;
		return this;
	}

	@Override
	public boolean equals(Object obj) {
		
		if (obj instanceof SocketHolder) {
			
			SocketHolder socketHolder = (SocketHolder)obj;
			
			return StringUtils.equals(this.accountInfo.getSrvNode(), socketHolder.accountInfo.getSrvNode()) 
					&& StringUtils.equals(this.accountInfo.getLoginName(), socketHolder.accountInfo.getLoginName())
					&& StringUtils.equals(this.accountInfo.getLoginPassword(), socketHolder.accountInfo.getLoginPassword())
					&& StringUtils.equals(this.accountInfo.getRandom(), socketHolder.accountInfo.getRandom());
		} else {
			return false;
		}
	}

	@Override
	public int hashCode() {
		
		int result = 0;
		
		if (this.accountInfo.getLoginName() != null) {
			result += this.accountInfo.getLoginName().hashCode();
		}
		
		if (this.accountInfo.getLoginPassword() != null) {
			result += this.accountInfo.getLoginPassword().hashCode();
		}
		
		if (this.accountInfo.getSrvNode() != null) {
			result += this.accountInfo.getSrvNode().hashCode();
		}
		
		if (this.accountInfo.getRandom() != null) {
			result += this.accountInfo.getRandom().hashCode();
		}
		
		return result;
	}
	
	public SocketHolder setForServer(boolean forServer) {
		this.forServer = forServer;
		return this;
	}

	public AbstractUserChecker getUserChecker() {
		return userChecker;
	}

	public void setUserChecker(AbstractUserChecker userChecker) {
		this.userChecker = userChecker;
	}

	public AccountInfo getAccountInfo() {
		return accountInfo;
	}

	public void setAccountInfo(AccountInfo accountInfo) {
		this.accountInfo = accountInfo;
	}

	public MessageDispacher getMessageDispacher() {
		return messageDispacher;
	}

	public void setMessageDispacher(MessageDispacher messageDispacher) {
		this.messageDispacher = messageDispacher;
	}
}
